﻿using Gwn.Library.MvpVm.Base;
using Gwn.Library.MvpVm.Constants;
using Gwn.Library.MvpVm.Entities;
using Gwn.Library.MvpVm.Events;
using Gwn.Library.MvpVm.Extensions;
using Gwn.Library.MvpVm.ViewModels;
using Gwn.Module.UserManager.Interfaces;
using Gwn.Module.UserManager.Types;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Unity;

namespace Gwn.Module.UserManager.Commands
{
    public class UserManagerCommand : CommandBase
    {
        private SubscriptionToken _processEventToken;

        #region [Dependency] Property: Bll 
        /// <summary>
        /// Gets or sets the BLL.
        /// </summary>
        /// <value>The BLL.</value>
        [Dependency]
        public IBusinessLogicLayer Bll { get; set; }
        #endregion 

        #region Property: UserViewModel 
        /// <summary>
        /// Gets the user view model.
        /// </summary>
        /// <value>The user view model.</value>
        public UserCommandViewModel UserViewModel
        {
            get
            {
                if (Presenter == null) return null;
                return Presenter.View.DataContext as UserCommandViewModel;
            }
        }
        #endregion 

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
        public override void Execute(object parameter)
        {
            base.Execute(parameter);
            
            // The BLL or DAL will raise ProcessEvents when they
            // are completed.  If successful the ViewModel will
            // have to be sync'd up to reflect the successful changes
            // HandleProcessEvents() below subscribes to these events.
            switch(parameter.ToString())
            {
                case ConstantUserManager.UserManagerAdd:
                    // Reset and update ViewModel state (for binding as applicable)
                    ResetViewModelState();
                    UserViewModel.IsAdding = true;

                    // We'll add a record to the ViewModel list - the record
                    // won't be committed (to BLL/DAL) until save is requested
                    var user = new User();
                    UserViewModel.UserList.Add(user);
                    
                    // Update selected user (clears fields)
                    UserViewModel.SelectedUser = user;

                    PublishMessage("Adding record");
                    break;

                case ConstantUserManager.UserManagerEdit:
                    // Reset and update ViewModel state (for binding as applicable)
                    ResetViewModelState();
                    UserViewModel.IsEditing = true;

                    PublishMessage("Editing record");
                    break;

                case ConstantUserManager.UserManagerSave:
                    // Add - create the record
                    if (UserViewModel.IsAdding)
                        Bll.CreateUserAsync(UserViewModel.CreateUserFromViewModel());

                    // Update the record
                    if (UserViewModel.IsEditing)
                        Bll.UpdateUserAsync(UserViewModel.CreateUserFromViewModel());
                    break;

                case ConstantUserManager.UserManagerUndo:

                    if (UserViewModel.IsAdding)
                        UserViewModel.UserList.Remove(UserViewModel.SelectedUser);
                    else
                    {
                        if (UserViewModel.CachedUser != null)
                        {
                            // Restore from cached data (during ResetState on Add/Edit)
                            var record = UserViewModel.CachedUser.DeserializeObject<User>();

                            // Update the backend with cached record
                            Bll.UpdateUserAsync(record);
                        }
                    }

                    PublishMessage("Undo Successful!");
                    break;

                case ConstantUserManager.UserManagerDelete:
                    // Delete the current record from the list
                    Bll.DeleteUserAsync(UserViewModel.SelectedUser);
                    break;
            }
        }

        /// <summary>
        /// Handles the process events AFTER the BLL/DAL are done
        /// </summary>
        /// <param name="e">The <see cref="Gwn.Library.MvpVm.Events.ProcessEventArgs"/> instance containing the event data.</param>
        public void HandleProcessEvents(ProcessEventArgs e)
        {
            // We'll only handle UserManagerProcessType
            var processType = e.ProcessType.GetProcessType<UserManagerProcessType>();

            if (e.IsError)
                return;

            switch (processType)
            {
                case UserManagerProcessType.CreateData:
                case UserManagerProcessType.UpdateData:
                case UserManagerProcessType.ReadData:
                    UserViewModel.UpdateViewModelFromNewUserRecord(e.GetData<User>());
                    break;

                // Delete the user from the view model list
                case UserManagerProcessType.DeleteData:
                    UserViewModel.DeleteUserFromViewModelList(e.GetData<User>());
                    break;
            }
            
        }

        /// <summary>
        /// Called when [event aggregator set].  
        /// </summary>
        protected override void OnEventAggregatorSet()
        {
            // Subscribe to the ProcessEvent
            _processEventToken = EventAggregator.GetEvent<ProcessEvent>().Subscribe(HandleProcessEvents);
        }

        /// <summary>
        /// Called when [dispose].  Remove the subscription
        /// </summary>
        protected override void OnDispose()
        {
            // Unsubscribe from the ProcessEvent
            EventAggregator.GetEvent<ProcessEvent>().Unsubscribe(_processEventToken);
        }

        private void ResetViewModelState()
        {
            UserViewModel.CachedUser = ((object)UserViewModel.SelectedUser).SerializeObject();
            UserViewModel.IsEditing = false;
            UserViewModel.IsAdding = false;
        }

        private void PublishMessage(string message)
        {
            EventAggregator.GetEvent<ProcessEvent>().Publish(new ProcessEventArgs
            {
                ProcessType = UserManagerProcessType.MessageOnly,
                FriendlyMessage = message
            });

        }

    }
}
